package com.relax.mybatis.encrypt.algorithm;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.relax.mybatis.constant.EncryptConstants;
import com.relax.mybatis.encrypt.util.BouncyCastleRegister;

/**
 * @author relax
 * @date 2022/07/05
 * @desc SM4加密
 */
public class SM4 {

    public static final String KEY_ALGORITHM = "SM4";
    public static final String ALGORITHM = "SM4/ECB/PKCS5Padding";

    public static String generateKey() throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
        keyGenerator.init(128, new SecureRandom());
        return Base64.encodeToString(keyGenerator.generateKey().getEncoded());
    }

    public static SecretKeySpec generateKey(String key) {
        byte[] bytes = new byte[16];
        byte i = 0;
        for (byte b : key.getBytes(EncryptConstants.DEFAULT_CHARSET))
            bytes[i++ % 16] ^= b;
        return new SecretKeySpec(bytes, KEY_ALGORITHM);
    }

    public static String encryptToBase64(String data, String key) throws Exception {
        return Base64.encodeToString(deal(data.getBytes(EncryptConstants.DEFAULT_CHARSET), key, Cipher.ENCRYPT_MODE));
    }

    public static String encryptToHex(String data, String key) throws Exception {
        return Hex.encodeToString(deal(data.getBytes(EncryptConstants.DEFAULT_CHARSET), key, Cipher.ENCRYPT_MODE));
    }

    public static String decryptFromBase64(String data, String key) throws Exception {
        return new String(deal(Base64.decode(data), key, Cipher.DECRYPT_MODE), EncryptConstants.DEFAULT_CHARSET);
    }

    public static String decryptFromHex(String data, String key) throws Exception {
        return new String(deal(Hex.decode(data), key, Cipher.DECRYPT_MODE), EncryptConstants.DEFAULT_CHARSET);
    }

    private static byte[] deal(byte[] data, String key, int mode) throws Exception {
        SecretKeySpec secretKeySpec = generateKey(key);
        Cipher cipher = Cipher.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
        cipher.init(mode, secretKeySpec);
        return cipher.doFinal(data);
    }

    static {
        BouncyCastleRegister.register();
    }
}
